package org.zetaframework.mq.utils;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.messaging.Message;
import org.zetaframework.core.utils.ContextUtil;
import org.zetaframework.core.utils.ZetaFunction;

/**
 * 消息工具类
 *
 * @author gcc
 */
public final class MessageUtil {
    private static final Logger logger = LoggerFactory.getLogger(MessageUtil.class);

    /**
     * 从请求头中获取token值
     *
     * @param msg 消息对象
     * @return token
     * @param <T> 消息体类型
     */
    public static <T> String getToken(Message<T> msg) {
        // 获取token
        Object obj = msg.getHeaders().get(StpUtil.getTokenName());
        if (ObjectUtil.isNull(obj)) { return StrUtil.EMPTY; }

        // 转为字符串
        return Convert.toStr(obj, StrUtil.EMPTY);
    }


    /**
     * MQ消息处理模板方法
     * <p>
     * 说明：
     * <pre>
     * 使用模板处理消息
     * 将消息消费者中的通用逻辑抽离成模板，减少重复代码
     * </pre>
     *
     * 通用逻辑：
     * <pre>
     * 1. 获取请求头中的token
     * 2. 设置token到Threadlocal中
     * 3. 处理业务逻辑    <--- 你只需要处理这一步
     * 4. 清空Threadlocal中的token
     * </pre>
     *
     * 使用方法：
     * <pre>{@code
     *    @Bean
     *    public Consumer<Message<String>> consumer() {
     *         return msg -> {
     *             // 处理消息
     *             MessageUtil.handleMessage("", msg, () -> {
     *                 // todo: 处理业务逻辑
     *                 String state = msg.getPayload();
     *             });
     *         };
     *     }
     * }</pre>
     *
     * @param eventName 事件名称（用于日志）
     * @param msg 消息对象
     * @param handler 业务处理函数
     */
    public static <T> void handleMessage(String eventName, Message<T> msg, ZetaFunction handler) {
        try {
            // 安全地获取 token 值
            String tokenValue = MessageUtil.getToken(msg);
            if (StrUtil.isBlank(tokenValue)) {
                if (StrUtil.isNotBlank(eventName)) {
                    logger.error("[{}]消息消费者：token获取失败", eventName);
                }
                return;
            }
            ContextUtil.setToken(tokenValue);

            // 执行业务逻辑
            handler.run();
        } catch (Exception e) {
            e.printStackTrace();
            if (StrUtil.isNotBlank(eventName)) {
                logger.error("[{}]消息消费者：处理失败，{}", eventName, e.getMessage());
            }
        } finally {
            // 清空ThreadLocal的值，防止下次请求时获取到的值是旧数据，同时也能防止内存溢出
            ContextUtil.remove();
        }
    }

}
